import { DynamicConditionAST } from '../types/node';
import { isFunction } from '/@/utils/is';

type LocalStorageConf = {
  storageKey: string;
};

type ApiStorageConf = {
  getAllApi: () => Promise<DynamicConditionAST[]>;
  getApi: (key: string) => Promise<DynamicConditionAST>;
  updateApi: (condition: DynamicConditionAST) => Promise<any>;
  saveApi: (condition: DynamicConditionAST) => Promise<any>;
  delApi: (key: string) => Promise<any>;
  // api请求结果字段 支持 xx.xx.xx
  resultField: string;
};

export type ConditionStorageOption = {
  mode: 'localstorage' | 'api';
  conf: LocalStorageConf | ApiStorageConf;
};

type ConditionStoreReturnType = {
  // 获取所有条件AST
  getConditionASTs: () => Promise<DynamicConditionAST[]>;
  // 根据key获取条件AST
  getConditionAST: (key: string) => Promise<DynamicConditionAST | null | undefined>;
  // 保存条件AST
  saveConditionAST: (
    condition: DynamicConditionAST,
  ) => Promise<DynamicConditionAST | null | undefined>;
  // 更新条件AST
  updateConditionAST: (
    condition: DynamicConditionAST,
  ) => Promise<DynamicConditionAST | null | undefined>;
  // 删除条件AST
  delConditionAST: (key: string) => Promise<any>;
};

function validate(target: Function, msg: string) {
  if (!target) {
    console.error(`${msg}`);
    throw new Error(`${msg || 'conditionStore validate error'}`);
  }
  if (!isFunction(target)) {
    console.error(`${msg}`);
    throw new Error(`${msg || 'conditionStore validate error：必须是一个function'}`);
  }
}

/**
 * <p>
 * 获取Api结果
 * </p>
 * @version 1.0.0 <br>
 * @date 2024/5/19 17:31 <br>
 * @author yaolonga <br>
 * @param Promise<any>
 * @return
 */
function getApiResult(api: (...arg: any) => Promise<any>, resultField: string) {
  const path = resultField ? resultField.split('.') : [];
  return api().then((res) => {
    if (res) {
      return path.reduce((previousValue, currentValue) => {
        return previousValue[currentValue];
      }, res);
    }
    return res;
  });
}

export function useConditionASTStore(option: ConditionStorageOption): ConditionStoreReturnType {
  async function getConditionASTs(): Promise<DynamicConditionAST[]> {
    if (option.conf) {
      if (option.mode == 'localstorage') {
        const conf = option.conf as LocalStorageConf;
        const conditionJson = localStorage.getItem(conf.storageKey) || '[]';
        try {
          return JSON.parse(conditionJson) as DynamicConditionAST[];
        } catch (e) {
          console.error('getConditions error ', e);
          return Promise.resolve([]);
        }
      } else if (option.mode == 'api') {
        const conf = option.conf as ApiStorageConf;
        validate(conf.getAllApi, 'property getAllApi not null');
        return getApiResult(conf.getAllApi, conf.resultField);
      }
    }
    return Promise.resolve([]);
  }

  async function getConditionAST(key: string): Promise<DynamicConditionAST | undefined | null> {
    if (option.conf) {
      if (option.mode == 'localstorage') {
        const conditions = await getConditionASTs();
        return conditions.find((condition) => condition.key == key);
      } else if (option.mode == 'api') {
        const conf = option.conf as ApiStorageConf;
        validate(conf.getApi, 'property getApi not null');
        return getApiResult(conf.getApi.bind(null, key), conf.resultField);
      }
    }
    return null;
  }

  async function saveConditionAST(
    condition: DynamicConditionAST,
  ): Promise<DynamicConditionAST | undefined | null> {
    if (option.conf) {
      if (option.mode == 'localstorage') {
        const conf = option.conf as LocalStorageConf;
        const conditions = await getConditionASTs();
        if (!condition) {
          return null;
        }
        if (conditions.length > 0) {
          conditions.push(condition);
          localStorage.setItem(conf.storageKey, JSON.stringify(conditions));
        } else {
          const conditionASTs = [condition];
          localStorage.setItem(conf.storageKey, JSON.stringify(conditionASTs));
        }
        return condition;
      } else if (option.mode == 'api') {
        const conf = option.conf as ApiStorageConf;
        validate(conf.saveApi, 'property saveApi not null');
        return getApiResult(conf.saveApi.bind(null, condition), conf.resultField).then((res) => {
          if (!res) {
            return condition;
          }
          return res;
        });
      }
    }
    return null;
  }

  async function updateConditionAST(
    condition: DynamicConditionAST,
  ): Promise<DynamicConditionAST | undefined | null> {
    if (option.conf) {
      if (option.mode == 'localstorage') {
        const conf = option.conf as LocalStorageConf;
        const _conditions = await getConditionASTs();
        const _condition = _conditions.find((c) => c.key == condition.key);
        if (_condition) {
          _condition.groups = condition.groups;
          localStorage.setItem(conf.storageKey, JSON.stringify(_conditions));
          return condition;
        }
        return null;
      } else if (option.mode == 'api') {
        const conf = option.conf as ApiStorageConf;
        validate(conf.updateApi, 'property updateApi not null');
        return getApiResult(conf.updateApi.bind(null, condition), conf.resultField).then((res) => {
          if (!res) {
            return condition;
          }
          return res;
        });
      }
    }
    return null;
  }

  async function delConditionAST(key: string) {
    if (option.conf) {
      if (option.mode == 'localstorage') {
        const conf = option.conf as LocalStorageConf;
        const _conditions = await getConditionASTs();
        const delIndex = _conditions.findIndex((c) => c.key == key);
        if (delIndex != -1) {
          _conditions.splice(delIndex, 1);
          localStorage.setItem(conf.storageKey, JSON.stringify(_conditions));
          return delIndex;
        }
        return null;
      } else if (option.mode == 'api') {
        const conf = option.conf as ApiStorageConf;
        validate(conf.delApi, 'property updateApi not null');
        return getApiResult(conf.delApi.bind(null, key), conf.resultField);
      }
    }
    return null;
  }

  return {
    getConditionASTs,
    getConditionAST,
    saveConditionAST,
    updateConditionAST,
    delConditionAST,
  };
}
